home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Diamond Collection / The Diamond Collection (Software Vault)(Digital Impact).ISO / cdr44 / 2m30src.zip / 2MKERNEL.INC < prev    next >
Text File  |  1995-03-06  |  82KB  |  1,964 lines

  1.  
  2. ;┌───────────────────────────────────────────────────────────────────┐
  3. ;│                                                                   │
  4. ;│           █████ █   █ █  █  █▀▀▀▀ █▀▀▄  █   █ █▀▀▀▀ █             │
  5. ;│               █ ██ ██ █ █   █     █   █ ██  █ █     █             │
  6. ;│           █████ █ █ █ ██▄   █████ ████  █ █ █ █████ █             │
  7. ;│           █     █   █ █  █  █     █   █ █  ██ █     █             │
  8. ;│           █████ █   █ █   █ █▄▄▄▄ █   █ █   █ █▄▄▄▄ █████         │
  9. ;│                                                                   │
  10. ;│     2MKERNEL 3.0  -  (C) 1993-1995  Ciriaco García de Celis.      │
  11. ;│                                                                   │
  12. ;│  NUCLEO RESIDENTE DE 2M UTILIZADO POR SUS PRINCIPALES EJECUTABLES │
  13. ;│                                                                   │
  14. ;│                                                                   │
  15. ;│    Los siguientes símbolos se utilizan                            │
  16. ;│    para el ensamblaje condicional:                                │
  17. ;│                                                                   │
  18. ;│  XT -> Indica que el código ejecutable es para PC/XT y no posee   │
  19. ;│        instrucciones de 286 ni utiliza recursos hardware de AT.   │
  20. ;│                                                                   │
  21. ;│  SUPERBOOT -> Indica que el código ejecutable se ensambla para    │
  22. ;│               ocupar 2560 bytes exactamente (para autoarranque).  │
  23. ;│                                                                   │
  24. ;└───────────────────────────────────────────────────────────────────┘
  25.  
  26.  
  27. ; ------------ Códigos de modos y órdenes del DMA y del FDC.
  28.  
  29. F_READ         EQU   46h               ; modo DMA para lectura
  30. F_WRITE        EQU   4Ah               ; modo DMA para escritura
  31. F_VERIFY       EQU   42h               ; modo DMA para verificación
  32. F_FORMAT       EQU   01001101b         ; orden de formateo del FDC
  33. FD_STATUS      EQU   3F4h              ; registro de estado
  34. FD_DOR         EQU   3F2h              ; registro de salida digital
  35. FD_DCR         EQU   3F7h              ; registro de control disco
  36.  
  37. ; ------------ Estructura de datos con información para cada unidad.
  38.  
  39. info_drv       STRUC
  40. maxs           EQU   13           ; máximo 13 sectores físicos/pista
  41. tipo_drv       DB    ?            ; tipo de la disquetera (0 = no hay)
  42. control2m_flag DB    OFF          ; a ON si 2M controla la unidad
  43. cambio         DB    ON           ; a ON indica cambio de soporte
  44. version_fmt    DB    ?            ; versión del formato de disco 2M
  45. multi_io       DB    ?            ; a 0 si posible acceso multi-sector
  46. chk            DB    ?            ; a 0 si checksum del sector 0 Ok
  47. vunidad        EQU   THIS WORD
  48. vunidad0       DB    ?            ; velocidad pista 0
  49. vunidadx       DB    ?            ; velocidad demás pistas
  50. gap            DB    ?            ; GAP entre sectores (leer/escribir)
  51. sectpista      DB    ?            ; sectores lógicos por pista
  52. tabla_tsect    DB    maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
  53. tam_fat        DB    ?            ; sectores/FAT en la unidad
  54.                ENDS
  55.  
  56. ; ------------ Variables del programa.
  57.  
  58. info_ptr       DW    info_A       ; punteros a datos de las unidades
  59.                DW    info_B
  60.               IFDEF SUPERBOOT
  61.                DB    "30"         ; Versión SuperBOOT 3.0
  62.               ENDIF
  63. id_sistema     DB    "2M-STV"     ; identificación de disco 2M
  64.               IFDEF XT
  65. tbase          DW    ?            ; base de tiempos para retardos
  66.               ENDIF
  67. unidad         DB    ?            ; unidad física de disco en curso
  68. numsect        DW    ?            ; sectores a transferir
  69. sectini        DW    ?            ; primer sector DOS a transferir
  70. cilindro       DB    ?            ; cilindro del disco a acceder
  71. cabezal        DB    ?            ; cabezal a emplear
  72. sector         DB    ?            ; número de sector físico
  73. sector_ini     DB    ?            ; número de sector físico inicial
  74. sector_fin     DB    ?            ; número de sector físico final
  75. seccion        DB    ?            ; parte del sector físico en curso
  76. secciones      DB    ?            ; sectores lógicos a transferir
  77. tsector        DB    ?            ; LOG2 (tamaño de sector) - 7
  78. buffer         DW    buffer_io    ; puntero al buffer intermedio
  79. buf_unidad     DB    ?            ; unidad del sector en el buffer
  80. buf_cilcab     DW    ?            ; cilindro/cabezal de sector buffer
  81. buf_sector     DB    ?            ; número de sector en el buffer
  82. status         DB    ?            ; resultado de los accesos a disco
  83. fdc_result     DB    7 DUP (?)    ; bytes de resultados del FDC
  84. orden          DB    ?            ; operación F_READ/F_WRITE/F_VERIFY
  85. tab_ordenes    DB    F_READ
  86.                DB    F_WRITE
  87.                DB    F_VERIFY     ; órdenes 2, 3 y 4
  88.  
  89.                ; --- Interpretación BIOS de los bits de ST1
  90.  
  91. lista_errs     DB    4            ; 'sector not found'
  92.                DB    0
  93.                DB    10h          ; 'bad CRC'
  94.                DB    8            ; 'DMA overrun'
  95.                DB    0
  96.                DB    4            ; 'sector not found'
  97.                DB    3            ; 'write-protect error'
  98.                DB    2            ; 'address mark not found'
  99.                DB    20h          ; en otro caso: 'bad NEC'
  100.  
  101. info_A         info_drv <>        ; datos de A:
  102. info_B         info_drv <>        ; datos de B:
  103.  
  104. ; ***************************************
  105. ; *                                     *
  106. ; *   C O D I G O   R E S I D E N T E   *
  107. ; *                                     *
  108. ; ***************************************
  109.  
  110. ; ------------ Rutina de gestión de INT 2Fh.
  111.  
  112.               IFNDEF SUPERBOOT   ; Código SuperBOOT no soporta INT 2Fh
  113.  
  114. ges_int2F      PROC  FAR
  115.                STI
  116.                CMP   AH,CS:multiplex_id
  117.                JE    preguntan
  118.                JMP   CS:ant_int2F      ; saltar al gestor de INT 2Fh
  119. preguntan:     CMP   DI,1992h
  120.                JNE   ret_no_info       ; no llama alguien del convenio
  121.                MOV   AX,ES
  122.                CMP   AX,1492h
  123.                JNE   ret_no_info       ; no llama alguien del convenio
  124.                PUSH  CS
  125.                POP   ES                ; sí llama: darle información
  126.                LEA   DI,autor_nom_ver
  127. ret_no_info:   MOV   AX,0FFFFh         ; "entrada multiplex en uso"
  128.                IRET
  129. ges_int2F      ENDP
  130.  
  131.               ENDIF
  132.  
  133. ; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
  134. ;              original o a una nueva rutina de control para la
  135. ;              lectura (AH=2), escritura (AH=3) y verificación (AH=4)
  136. ;              según el tipo de disco introducido. Ante una función de
  137. ;              formateo (AH=5) no 2M, se pasa el control a la INT 13h
  138. ;              original. Se detecta un posible cambio de disco y se
  139. ;              retorna en ese caso con el correspondiente error. En el
  140. ;              código SuperBOOT no hay soporte para formatear.
  141.  
  142.               IFNDEF SUPERBOOT
  143.  
  144. ges_int13      PROC  FAR
  145.                STI
  146.                CLD
  147.                PUSHF
  148.                CMP   DL,2
  149.                JAE   ges13bios         ; no es disquetera A: ó B:
  150.                PUSH  SI
  151.                CALL  set_SI_drv
  152.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  153.                JE    ges_2m
  154.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  155. ges_2m:        POP   SI
  156.                JC    ges13bios         ; no es unidad de alta densidad
  157.                CMP   AH,2
  158.                JB    ges13bios         ; no Read/Write/Verify/Format
  159.                CMP   AH,5
  160.                JA    ges13bios         ; no Read/Write/Verify/Format
  161.                CALL  detecta_cambio    ; ¿cambio de disco?
  162.                JNC   sin_cambio
  163.                POPF
  164.                STC                     ; hubo cambio:
  165.                MOV   AX,600h
  166.                RET   2                 ; retornar con error
  167. sin_cambio:    CMP   AH,5
  168.                JNE   dilucida          ; no es orden de formateo
  169.                CALL  leer_lin_camb
  170.                JNZ   format_bios       ; no hay disquete en la unidad
  171.                CMP   AL,7Fh
  172.                JNE   format_bios       ; no es orden formateo de 2M
  173.                CMP   SI,"2M"
  174.                JE    format_2m         ; es orden de formateo de 2M
  175. format_bios:   CLC
  176.                CALL  set_flag_STV      ; CF = 0 -> indicar no 2M
  177. dilucida:      PUSH  SI
  178.                CALL  set_SI_drv        ; SI -> variables de la unidad
  179.                CMP   CS:[SI].control2m_flag,OFF
  180.                POP   SI
  181.                JE    ges13bios         ; la unidad la controla la BIOS
  182.                POPF
  183.                CALL  control2m         ; la controla 2M
  184.                RET   2
  185. ges13bios:     POPF
  186.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  187.  
  188.                ; --- Función de formateo implementada por 2M. En los
  189.                ;     disquetes creados con /M todas las pistas salvo
  190.                ;     la 0 deberían ser formateadas invocando INT 13h
  191.                ;     de manera directa (con CALL) para evitar que se
  192.                ;     ejecute cierto código de WINDOWS en el modo
  193.                ;     protegido que provoca errores al formatear. Antes
  194.                ;     de formatear la primera pista física del disco se
  195.                ;     invoca la función de formateo de la INT 13h
  196.                ;     original con objeto de informar al DOS y a todos
  197.                ;     los TSR previos del cambio de soporte.
  198.                ;     El intento de formateo en la pista/cabezal 0 con
  199.                ;     CL=255 sirve para simular un cambio de disco.
  200.  
  201. format_2m:     POPF
  202.                PUSH  DS                ; *
  203.                XPUSHA                  ; **
  204.                PUSH  CS
  205.                POP   DS
  206.                MOV   unidad,DL
  207.                CALL  set_SI_drv
  208.                MOV   cilindro,CH
  209.                MOV   cabezal,DH
  210.                OR    CH,DH
  211.                JNZ   format_trx        ; no es cilindro 0 y cabezal 0
  212.                INC   CL
  213.                JNZ   no_f_chg
  214.                MOV   [SI].cambio,ON    ; simular cambio de disco
  215.                JMP   fmt_exit
  216. no_f_chg:      XPUSHA
  217.                PUSHF                   ; formatear (AH=5) para
  218.                CALL  ant_int13         ; avisar al DOS del nuevo disco
  219.                CLD                     ; mantener DF=0
  220.                STC
  221.                CALL  reset_drv         ; asegurar aceleración motor
  222.                XPOPA
  223.                CALL  set_info          ; características nuevo soporte
  224. format_trx:    CALL  genera_info       ; tabla de información formateo
  225.                CALL  motor_ok          ; asegurar que está en marcha
  226.                CALL  seek_drv
  227.                CALL  formatea_pista
  228.                PUSHF
  229.                CLC
  230.                CALL  motor_off_cnt     ; cuenta normal detención motor
  231.                POPF
  232.                CALL  set_err
  233.                CALL  set_bios_err      ; no altera flags
  234. fmt_exit:      XPOPA                   ; **
  235.                MOV   AH,status
  236.                POP   DS                ; *
  237.                RET   2
  238. ges_int13      ENDP
  239.  
  240.               ELSE   ; El código SuperBOOT no formatea
  241.  
  242. ges_int13      PROC  FAR
  243.                STI
  244.                CLD
  245.                PUSHF
  246.                PUSH  SI
  247.                CMP   DL,2
  248.                JAE   ges13bios         ; no es disquetera A: ó B:
  249.                CALL  set_SI_drv
  250.                CMP   CS:[SI].tipo_drv,2  ; ¿unidad 1.2M?
  251.                JE    ges_2m
  252.                CMP   CS:[SI].tipo_drv,4  ; ¿unidad 1.44/2.88M?
  253. ges_2m:        JC    ges13bios         ; no es unidad de alta densidad
  254.                CMP   AH,2
  255.                JB    ges13bios         ; no Read/Write/Verify/Format
  256.                CMP   AH,5
  257.                JA    ges13bios         ; no Read/Write/Verify/Format
  258.                JNE   no_format
  259.                CALL  set_flag_STV      ; CF = 0 -> "disco no 2M"
  260.                JMP   ges13bios
  261. no_format:     CALL  detecta_cambio    ; ¿cambio de disco?
  262.                JNC   dilucida
  263.                POP   SI
  264.                POPF
  265.                STC                     ; hubo cambio:
  266.                MOV   AX,600h
  267.                RET   2                 ; retornar con error
  268. dilucida:      CMP   CS:[SI].control2m_flag,OFF
  269.                JE    ges13bios         ; la unidad la controla la BIOS
  270.                POP   SI
  271.                POPF
  272.                CALL  control2m         ; la controla 2M
  273.                RET   2
  274. ges13bios:     POP   SI
  275.                POPF
  276.                JMP   CS:ant_int13      ; saltar al gestor de INT 13h
  277. ges_int13      ENDP
  278.  
  279.               ENDIF
  280.  
  281. ; ------------ A la entrada en DL se indica la unidad y a la salida se
  282. ;              devuelve SI apuntando sus variables sin alterar flags.
  283.  
  284. set_SI_drv     PROC
  285.                PUSHF
  286.                PUSH  BX
  287.                MOV   BL,DL
  288.                MOV   BH,0
  289.                SHL   BX,1
  290.                MOV   SI,CS:[BX+OFFSET info_ptr]
  291.                POP   BX
  292.                POPF
  293.                RET
  294. set_SI_drv     ENDP
  295.  
  296. ; ------------ Si CF=1, indicar disquete 2M presente. A la
  297. ;              entrada, DL indica la unidad de disco.
  298.  
  299. set_flag_STV   PROC
  300.                XPUSHA
  301.                CALL  set_SI_drv
  302.                MOV   AL,ON                       ; indicar 2M
  303.                JC    tipo_stv_ok
  304.                MOV   AL,OFF                      ; indicar no 2M
  305. tipo_stv_ok:   MOV   CS:[SI].control2m_flag,AL
  306.                XPOPA
  307.                RET
  308. set_flag_STV   ENDP
  309.  
  310. ; ------------ Devolver ZF=1 si cilindro y cabezal 0.
  311.  
  312. pista0?        PROC
  313.                PUSH  AX
  314.                MOV   AL,cabezal
  315.                OR    AL,cilindro
  316.                POP   AX
  317.                RET
  318. pista0?        ENDP
  319.  
  320. ; ------------ Devolver ZF=1 si la línea de cambio de disco está
  321. ;              inactiva. A la entrada, DL contiene la unidad. El
  322. ;              motor es puesto en marcha y, si no lo estaba ya, la
  323. ;              variable que indica lo que resta para detenerlo
  324. ;              es llevada a su valor normal, por lo que el disco no
  325. ;              tardará mucho en detenerse (incluso sin quizá haber
  326. ;              acelerado aún). En la práctica, invocando esta rutina
  327. ;              desde INT 13h nunca será necesario arrancar el motor
  328. ;              ya que el DOS ejecuta antes la función equivalente,
  329. ;              la 16h, que lo pone en marcha. Es simplemente una
  330. ;              medida de seguridad contra las BIOS «de marca».
  331.  
  332. leer_lin_camb  PROC
  333.                XPUSHA                  ; *
  334.                PUSH  DS
  335.                DDS
  336.                MOV   AL,1
  337.                MOV   CL,DL
  338.                SHL   AL,CL             ; bit de motor en 0..3
  339.                TEST  DS:[3Fh],AL
  340.                JNZ   rodando           ; el motor ya está girando
  341.                CLC
  342.                CALL  motor_off_cnt     ; cuenta normal detención motor
  343. rodando:       MOV   AH,DL
  344.                XSHL  AH,4
  345.                OR    AH,AL             ; AH = byte BIOS
  346.                XSHL  AL,4
  347.                OR    AL,00001100b      ; modo DMA, no hacer reset
  348.                OR    AL,DL             ; AL para reg. salida digital
  349.                MOV   DX,FD_DOR
  350.                CLI
  351.                MOV   DS:[3Fh],AH       ; actualizar variable BIOS
  352.                OUT   DX,AL             ; arrancado motor en la unidad
  353.                ADD   DX,5
  354.                DELAY
  355.                IN    AL,DX             ; leer línea de cambio de disco
  356.                STI
  357.                TEST  AL,80h            ; ZF=0 -> cambio de disco
  358.                POP   DS
  359.                XPOPA                   ; *
  360.                RET
  361. leer_lin_camb  ENDP
  362.  
  363. ; ------------ Determinar si ha habido cambio de disco y, en ese caso,
  364. ;              si el nuevo disquete es de tipo 2M o no. El cambio de
  365. ;              disco se detecta leyendo la línea de cambio de disco o
  366. ;              chequeando la variable que indica si ha habido cambio
  367. ;              o no (esta variable está a ON tras instalar 2M para
  368. ;              forzar la detección del tipo de disco introducido; se
  369. ;              pone en ON también si no se logra bajar la línea de
  370. ;              cambio de disco por si fuera un soporte raro y la BIOS
  371. ;              sí lo lograra -forzando así una detección posterior-).
  372.  
  373. detecta_cambio PROC
  374.                XPUSHA                  ; *
  375.                CALL  set_SI_drv        ; SI -> variables de la unidad
  376.                CMP   CS:[SI].cambio,ON ; ¿cambio de soporte?
  377.                MOV   CS:[SI].cambio,OFF
  378.                JE    hubo_cambio
  379.                CALL  leer_lin_camb     ; leer línea de cambio de disco
  380.                JNZ   hubo_cambio
  381.                XPOPA
  382.                CLC                     ; no hay cambio de disco
  383.                RET
  384. hubo_cambio:   CLC
  385.                CALL  set_flag_STV      ; CF = 0 -> supuesto no 2M
  386.                XPUSH <DS, ES>          ; **
  387.                MOV   BX,90h
  388.                ADD   BL,DL
  389.                DDS
  390.                AND   BYTE PTR [BX],255-16 ; densidad no determinada
  391.                XPUSH <CS, CS>
  392.                XPOP  <DS, ES>
  393.                MOV   unidad,DL
  394.                STC                     ; asegurar motor en marcha
  395.                CALL  reset_drv
  396.                MOV   cilindro,1
  397.                MOV   cabezal,0
  398.                CALL  seek_drv          ; bajar línea cambio de disco
  399.                DEC   cilindro
  400.                CALL  seek_drv
  401.                CLC
  402.                CALL  motor_off_cnt     ; cuenta normal detención motor
  403.                CALL  leer_lin_camb     ; ¿bajada línea cambio disco?
  404.                JZ    disco_dentro      ; se pudo: hay disco dentro
  405.                MOV   [SI].cambio,ON    ; futura detección tipo disco
  406.                CLC                     ; NO indicar cambio de disco...
  407.                JMP   fin_detecta       ; ...para pasar control a BIOS
  408. disco_dentro:  PUSH  DS
  409.                DDS
  410.                MOV   BYTE PTR DS:[41h],6  ; error 'media changed'
  411.                POP   DS
  412.               IFNDEF SUPERBOOT
  413.                CMP   AH,5              ; ¿función de formateo?
  414.                JE    fin_detecta_c     ; no perder el tiempo
  415.               ENDIF
  416.                MOV   buf_unidad,-1     ; invalidar buffer
  417.                MOV   [SI].gap,20       ; GAP provisional
  418.                MOV   CX,3              ; 3 intentos
  419. intenta_io0:   PUSH  CX
  420.                CMP   CX,2              ; CF=1 la 3ª vez (a 0 si CX<>1)
  421.                CALL  reset_drv
  422.                MOV   [SI].vunidad0,0   ; empezar con 500 Kbit/seg.
  423. intenta_io:    MOV   AL,0
  424.                MOV   cilindro,AL
  425.                MOV   cabezal,AL
  426.                MOV   sector,1          ; sector de arranque
  427.                MOV   seccion,AL
  428.                MOV   secciones,1
  429.                MOV   orden,F_READ
  430.                MOV   DI,buffer
  431.                CALL  direct_acceso
  432.                JNE   otra_densidad     ; es otra densidad de disco
  433.                POP   CX
  434.                MOV   BX,buffer
  435.                CALL  set_info          ; características nuevo soporte
  436.                CLC
  437.                JMP   fin_detecta_c     ; indicar cambio de disco
  438. otra_densidad: MOV   AL,[SI].vunidad0
  439.                INC   AX                ; próxima velocidad
  440.                CMP   AL,3
  441.                JA    otro_intento
  442.                MOV   [SI].vunidad0,AL
  443.                JMP   intenta_io        ; probar otra velocidad
  444. otro_intento:  MOV   [SI].vunidad0,0
  445.                POP   CX
  446.                LOOP  intenta_io0       ; reintento
  447. fin_detecta_c: STC                     ; indicar cambio de disco
  448. fin_detecta:   XPOP  <ES, DS>          ; **
  449.                XPOPA                   ; *
  450.                RET
  451. detecta_cambio ENDP
  452.  
  453. ; ------------ Anotar la información del disquete si es de tipo 2M.
  454. ;              A la entrada, DS:SI apunta a las variables de la unidad
  455. ;              y ES:BX al sector de arranque del disco. Se actualiza
  456. ;              también la variable BIOS de tipo de densidad (la BIOS
  457. ;              no se da cuenta del cambio de disco y conviene ayudar).
  458.  
  459. set_info       PROC
  460.                XPUSHA
  461.                CALL  calc_chk
  462.                JC    set_info_exit     ; no es disco 2M
  463.                MOV   [SI].chk,AL         ; anotar checksum
  464.                MOV   [SI].version_fmt,CL ; y versión del formato
  465.                MOV   DL,unidad
  466.                STC
  467.                CALL  set_flag_STV      ; CF = 1 -> indicar disco 2M
  468.                MOV   AL,ES:[BX+22]     ; tamaño de FAT
  469.                MOV   [SI].tam_fat,AL
  470.                MOV   CL,ES:[BX+65]     ; CL a 0 si acceso multi-sector
  471.                MOV   [SI].multi_io,CL
  472.                MOV   AX,ES:[BX+66]
  473.                MOV   [SI].vunidad,AX   ; velocidad pista 0 / demás
  474.                MOV   AL,ES:[BX+24]
  475.                MOV   [SI].sectpista,AL ; sectores/pista
  476.                MOV   DI,ES:[BX+72]
  477.                MOV   AL,ES:[BX+DI+1]   ; GAP de formateo
  478.                MOV   AH,AL
  479.                AND   CL,CL             ; CL a 0 si acceso multi-sector
  480.                JZ    gap_rw_ok         ; GAP R/W para /F
  481.                ADD   AH,190
  482.                MOV   AL,11
  483.                MUL   AH                ; AX = (190+GAP)*11
  484.                SUB   AX,2048+62
  485. gap_rw_ok:     SHR   AL,1              ; GAP R/W para /M
  486.                MOV   [SI].gap,AL
  487.                MOV   CX,maxs
  488.                MOV   DI,ES:[BX+74]
  489.                ADD   DI,BX
  490.                LEA   BX,[SI].tabla_tsect
  491. genera_ts:     MOV   AL,ES:[DI]
  492.                MOV   [BX],AL
  493.                INC   BX
  494.                INC   DI
  495.                LOOP  genera_ts         ; información estructura pistas
  496. set_info_exit: MOV   AL,[SI].vunidad0
  497.               IFDEF  XT
  498.                MOV   CL,6
  499.                SHL   AL,CL
  500.               ELSE
  501.                XSHL  AL,6              ; velocidad en bits 7:6
  502.               ENDIF
  503.                OR    AL,00010111b      ; establecido otro medio físico
  504.                CMP   [SI].tipo_drv,2
  505.                JA    modo_ok           ; es unidad de 3½
  506.                AND   AL,11111000b
  507.                OR    AL,00000101b      ; 1.2 en 1.2
  508.                TEST  AL,01000000b
  509.                JZ    modo_ok
  510.                XOR   AL,00100001b      ; 360 en 1.2 y seek * 2
  511. modo_ok:       PUSH  DS
  512.                MOV   BX,90h
  513.                ADD   BL,unidad
  514.                DDS
  515.                AND   BYTE PTR DS:[BX],8  ; respetar bit de 2.88M
  516.                OR    DS:[BX],AL        ; actualizar variable BIOS
  517.                POP   DS                ; con el tipo de densidad
  518.                XPOPA
  519.                RET
  520. set_info       ENDP
  521.  
  522. ; ------------ Calcular el checksum de la zona vital del sector de
  523. ;              arranque. A la entrada, ES:BX -> sector de arranque.
  524. ;              A la salida, CF=1 si el disco no es 2M; de otro modo
  525. ;              checksum en AL y versión del formato de disco en CL.
  526.  
  527. calc_chk       PROC
  528.                XPUSH <SI, DI>
  529.                LEA   DI,[BX+3]         ; DI=BX+3
  530.                LEA   SI,id_sistema
  531.                MOV   CX,6
  532.                REP   CMPSB             ; comparar identificación
  533.                STC
  534.                JNE   chk_ret           ; el disco no es 2M
  535.                XOR   AX,AX
  536.                MOV   CL,ES:[BX+64]     ; versión del formateador
  537.                CMP   CL,6
  538.                JB    chk_ok            ; no usaba este checksum
  539.                MOV   DI,ES:[BX+68]
  540. chk_sum:       DEC   DI
  541.                ADD   AL,ES:[BX+DI]
  542.                CMP   DI,63
  543.                JA    chk_sum
  544. chk_ok:        CLC
  545. chk_ret:       XPOP  <DI, SI>
  546.                RET
  547. calc_chk       ENDP
  548.  
  549. ; ------------ Determinar el tipo de error producido en el acceso.
  550.  
  551. set_err        PROC
  552.                XPUSHA
  553.                JNC   err_ret           ; no hay error
  554.                CMP   status,0          ; ¿'status' ya asignado?
  555.                JNE   err_retc          ; no cambiarlo si es así
  556.                MOV   AL,BYTE PTR fdc_result+1
  557.                AND   AL,10110111b      ; aislar condiciones de test
  558.                LEA   BX,lista_errs
  559.                MOV   CX,9
  560. busca_err:     MOV   AH,[BX]           ; código de error BIOS
  561.                SHL   AL,1
  562.                JC    err_ok            ; es ese error
  563.                INC   BX
  564.                LOOP  busca_err         ; buscar otro error
  565. err_ok:        OR    status,AH
  566. err_retc:      STC                     ; condición de error
  567. err_ret:       XPOPA
  568.                RET
  569. set_err        ENDP
  570.  
  571. ; ------------ Actualizar variables de error de la BIOS.
  572.  
  573. set_bios_err   PROC
  574.                PUSHF                   ; *
  575.                XPUSHA                  ; **
  576.                PUSH  ES                ; ***
  577.                DES
  578.                MOV   DI,41h            ; bytes de resultados del 765:
  579.                LEA   SI,status         ; variable BIOS de status y 7
  580.                MOVSW                   ; bytes (4 palabras)
  581.                MOVSW
  582.                MOVSW
  583.                MOVSW
  584.                POP   ES                ; ***
  585.                XPOPA                   ; **
  586.                POPF                    ; *
  587.                RET
  588. set_bios_err   ENDP
  589.  
  590. ; ------------ Realizar lecturas, escrituras y verificaciones: rutina
  591. ;              que sustituye el código de la BIOS para poder soportar
  592. ;              los formatos 2M. La operación puede quedar dividida en
  593. ;              tres fases: el fragmento anterior a la FAT2, la zona
  594. ;              correspondiente a la FAT2 (se ignora la escritura y se
  595. ;              simula su lectura leyendo la FAT1) y un último bloque
  596. ;              ubicado tras la FAT2. El sector de arranque es emulado
  597. ;              empleando el primer sector físico de la FAT2 (aunque en
  598. ;              los discos de versión de formato anterior a la 7 se usa
  599. ;              el sector de arranque verdadero -permitiendo escribirlo
  600. ;              sólo si es válido-). En cualquier caso, si el número de
  601. ;              cabezal tiene el bit 7 activo, se sobreentiende que el
  602. ;              programa que llama soporta disquetes 2M y no se emula
  603. ;              la FAT2 ni el sector de arranque, para permitirle
  604. ;              acceder al código SuperBOOT. Las coordenadas de la BIOS
  605. ;              se traducen a las unidades del DOS por mayor comodidad.
  606.  
  607. control2m      PROC
  608.                PUSH  DS                ; *
  609.                XPUSHA                  ; **
  610.                PUSH  CS
  611.                POP   DS
  612.                MOV   unidad,DL
  613.                CALL  set_SI_drv        ; SI -> variables de la unidad
  614.                CMP   [SI].chk,0
  615.                JE    chk_valido        ; checksum correcto en sector 0
  616.                MOV   status,40h        ; devolver 'Seek Error' al DOS
  617.                JMP   exit_2m_ctrl
  618. chk_valido:    PUSH  AX                ; ***
  619.                MOV   AH,0
  620.                MOV   numsect,AX        ; nº sectores
  621.                MOV   AL,CH             ; cilindro
  622.                SHL   AL,1
  623.                MOV   DL,DH
  624.                AND   DH,01111111b
  625.                ADD   AL,DH             ; cabezal físico
  626.                MUL   [SI].sectpista
  627.                ADD   AL,CL             ; sector
  628.                ADC   AH,0
  629.                DEC   AX                ; AX = nº sector DOS
  630.                MOV   sectini,AX        ; 0FFFFh si sector 0 (error)
  631.                MOV   DI,BX             ; ES:DI -> dirección
  632.                POP   BX                ; ***
  633.                MOV   BL,BH
  634.                MOV   BH,0
  635.                MOV   CL,[BX+OFFSET tab_ordenes-2]
  636.                MOV   orden,CL
  637.                SHL   DL,1
  638.                JC    acceso_final      ; cabezal >= 128: no emular
  639.                AND   AX,AX             ; ¿comienza en sector 0?
  640.                JNZ   io_emula          ; no
  641.                CMP   [SI].version_fmt,7
  642.                JB    boot_real         ; no soportado BOOT virtual
  643.                MOV   AL,[SI].tam_fat   ; AH = 0
  644.                INC   AX
  645.                MOV   CX,1              ; sector BOOT emulado en
  646.                CALL  ejecuta_io        ; el primer sector FAT2
  647.                JNE   fin_ctrl
  648. boot_fin_op:   DEC   numsect
  649.                INC   sectini
  650.                MOV   AX,sectini
  651.                JMP   io_emula
  652. boot_real:     CMP   orden,F_WRITE
  653.                JNE   io_emula
  654.                MOV   BX,DI             ; BOOT de 2M 1.3 y anteriores
  655.                CALL  calc_chk
  656.                JC    si_skip           ; no es de tipo 2M
  657.                AND   AL,AL
  658.                JZ    io_emula          ; lo es y con checksum correcto
  659. si_skip:       ADD   DI,512
  660.                JMP   boot_fin_op       ; impedir estropicio de BOOT
  661. io_emula:      MOV   CL,[SI].tam_fat
  662.                MOV   CH,0              ; CX = primer sector FAT2 - 1
  663.                CMP   AX,CX
  664.                JA    en_fat2?          ; ¿la operación afecta a FAT2?
  665.                CALL  calc_iop          ; calcular sectores antes FAT2
  666.                CALL  ejecuta_io        ; CX sectores desde AX
  667.                JNE   fin_ctrl          ; error
  668.                CMP   numsect,0
  669.                JE    fin_ctrl          ; fin de la transferencia
  670. en_fat2?:      MOV   AX,sectini
  671.                MOV   CL,[SI].tam_fat
  672.                MOV   CH,0
  673.                SHL   CX,1              ; CX = último sector FAT2
  674.                CMP   AX,CX
  675.                JA    acceso_final      ; la operación es tras la FAT2
  676.                CALL  calc_iop          ; sectores hasta fin de FAT2
  677.                CMP   orden,F_WRITE
  678.                JNE   emula_fat1
  679.               IFDEF  XT
  680.                XCHG  CH,CL
  681.                SHL   CH,1
  682.               ELSE
  683.                XSHL  CX,9              ; CX = CX * 512
  684.               ENDIF
  685.                ADD   DI,CX             ; ES:DI actualizado
  686.                JMP   acceso_final
  687. emula_fat1:    MOV   DL,[SI].tam_fat
  688.                MOV   DH,0
  689.                SUB   AX,DX             ; leer de FAT1 y no de la FAT2
  690.                CALL  ejecuta_io        ; CX sectores desde AX
  691.                JNE   fin_ctrl          ; error
  692. acceso_final:  CMP   numsect,0
  693.                JE    fin_ctrl          ; fin de la transferencia
  694.                MOV   AX,sectini
  695.                MOV   CX,numsect
  696.                CALL  ejecuta_io
  697. fin_ctrl:      CLC
  698.                CALL  motor_off_cnt     ; cuenta normal detención motor
  699.                CALL  set_bios_err      ; actualizar variables BIOS
  700. exit_2m_ctrl:  XPOPA                   ; **
  701.                MOV   AH,status
  702.                POP   DS                ; *
  703.                AND   AH,AH
  704.                JZ    st_ok             ; resultado correcto (CF=0)
  705.                STC                     ; error
  706.                MOV   AL,0              ; 0 sectores movidos
  707. st_ok:         RET
  708. calc_iop:      SUB   CX,AX
  709.                INC   CX                ; CX sectores
  710.                CMP   CX,numsect
  711.                JBE   nsect_ok
  712.                MOV   CX,numsect        ; sólo quedan CX
  713. nsect_ok:      SUB   numsect,CX
  714.                ADD   sectini,CX
  715.                RET
  716. control2m      ENDP
  717.  
  718. ; ------------ A la entrada, AX indica el sector inicial (coordenadas
  719. ;              del DOS) y CX el número de sectores a procesar.
  720. ;              * Definiciones: «Sector físico» es un sector del disco
  721. ;              de 512, 1024 ó 2048 bytes (números de sector del 1 al N
  722. ;              en la pista). Este sector físico está dividido en
  723. ;              «secciones» de 512 bytes, constando por tanto de 1, 2 ó
  724. ;              4 secciones. «Sector virtual» es el número de sector
  725. ;              del programa que llama a INT 13h, comprendido entre 1 y
  726. ;              M. Esta estructura de N sectores por pista de distintos
  727. ;              tamaños, se verifica en todo el disco con excepción del
  728. ;              cabezal y cilindro 0 (con un formato más convencional
  729. ;              de sectores de 512 bytes numerados de 1 a J, aunque no
  730. ;              existen algunos de los intermedios que corresponden a
  731. ;              la segunda copia de la FAT).
  732. ;              * Primero se convierte el sector virtual (1..M) en su
  733. ;              correspondiente físico (1..J en la pista 0 y 1..N en
  734. ;              las demás), deduciendo qué porción de 512 bytes (o
  735. ;              sección) es afectada. Un sector virtual (512 bytes)
  736. ;              simulado suele ser parte de un sector físico de 2048
  737. ;              bytes en muchos casos. Si dicho sector físico ya había
  738. ;              sido leído al buffer en anteriores accesos, se extrae
  739. ;              la sección necesaria. Si no, se carga del disco y se
  740. ;              extrae dicho fragmento. El número de sectores virtuales
  741. ;              que se solicitan (=secciones) permite realizar un bucle
  742. ;              hasta completar la transferencia; el interleave 1:2 de
  743. ;              los sectores físicos en /M permite acceder sector a
  744. ;              sector sin pérdida de rendimiento. En el caso de la
  745. ;              escritura, se estudia primero si hay varios sectores
  746. ;              virtuales consecutivos que escribir, completando entre
  747. ;              todos un sector físico: en ese caso, se prepara el
  748. ;              mismo y se escribe sin más. En caso de que haya que
  749. ;              modificar sólo una única sección de un sector físico,
  750. ;              salvo si éste es de 512 bytes, no hay más remedio que
  751. ;              cargarlo al buffer (realizar una prelectura),
  752. ;              actualizar la sección correspondiente y volverlo a
  753. ;              escribir.
  754. ;              * En el formato /F se realiza una operación multisector
  755. ;              si es posible y sin emplear el buffer intermedio (si
  756. ;              bien podría ser preciso emplearlo con la primera y
  757. ;              última sección); en los dos formatos de disco se hace
  758. ;              la operación multisector en la primera pista. Las
  759. ;              operaciones multisector puede que sea preciso
  760. ;              dividirlas en tres fases: los sectores antes de una
  761. ;              frontera de DMA, el que la cruza (que es transferido
  762. ;              a través del buffer intermedio) y los que están detrás.
  763.  
  764. ejecuta_io     PROC
  765.                MOV   BX,AX             ; AX = sector DOS inicial
  766.                CMP   AH,0FFh
  767.                JE    no_cabe           ; (acceso a sector BIOS 0)
  768.                MOV   secciones,CL      ; CX sectores (CL realmente)
  769.                DIV   [SI].sectpista
  770.                INC   AH                ; numerado desde 1...
  771.                MOV   sector,AH         ; ...el resto es el sector
  772.                SHR   AL,1
  773.                MOV   cilindro,AL       ; cilindro
  774.                RCL   AL,1
  775.                AND   AL,1
  776.                MOV   cabezal,AL        ; cabezal
  777.                MOV   AL,sector
  778.                ADD   AL,secciones
  779.                JC    no_cabe           ; sector+secciones > 255
  780.                DEC   AX                ; DEC AX = DEC AL
  781.                CMP   AL,[SI].sectpista
  782.                JBE   si_cabe
  783. no_cabe:       MOV   status,4          ; 'sector no encontrado'
  784.                JMP   fin_io
  785. si_cabe:       MOV   AL,AH             ; sector en AL
  786.                CBW                     ; sección 0 (AH = 0)
  787.                CALL  pista0?
  788.                JZ    s_xx              ; sector físico en pista/cara 0
  789.                LEA   BX,[SI].tabla_tsect-1
  790.                DEC   AX                ; AH = 0
  791. resta_secc:    INC   BX
  792.                INC   AH
  793.                MOV   CL,[BX]
  794.                SUB   CL,2
  795.                MOV   CH,1
  796.                SHL   CH,CL
  797.                SUB   AL,CH
  798.                JNC   resta_secc        ; en las demás pistas
  799.                ADD   AL,CH
  800.                XCHG  AH,AL
  801. s_xx:          MOV   sector,AL         ; sector lógico convertido a
  802.                MOV   seccion,AH        ; sector y sección físicas
  803. direct_acceso: CALL  motor_ok          ; asegurar que está en marcha
  804.                MOV   AH,0
  805.                MOV   sector_fin,AH     ; no acceder a más de 1 sector
  806.                CALL  pista0?           ; (al menos de momento)
  807.                JNZ   decide_multi      ; no es pista 0
  808.                MOV   AL,secciones
  809.                MOV   secciones,AH      ; las que restan (AH = 0)
  810.                JMP   multi_proc
  811. decide_multi:  CMP   [SI].multi_io,AH  ; AH = 0
  812.                JNE   io_pasos          ; acceso sector a sector
  813.                CMP   seccion,AH
  814.                JE    multi_acc
  815.                CALL  acceso_secc       ; no acceso a inicio sector
  816.                JC    fin_io
  817. multi_acc:     CMP   secciones,AH      ; AH = 0
  818.                JE    fin_io
  819.                CALL  num_secciones
  820.                MOV   CL,AL
  821.                MOV   AL,secciones      ; AH = 0
  822.                DIV   CL
  823.                AND   AL,AL
  824.                JZ    io_pasos          ; no quedan sectores enteros
  825.                MOV   secciones,AH      ; las que restan
  826. multi_proc:    CALL  acceso_multi      ; de AL sectores
  827.                JC    fin_io
  828. io_pasos:      CMP   secciones,0
  829.                JE    fin_io            ; no restan secciones finales
  830.                CALL  acceso_secc
  831.                JNC   io_pasos
  832. fin_io:        CMP   status,0          ; ZF = 1 -> operación correcta
  833.                RET
  834.  
  835. acceso_secc:   PUSH  AX
  836.                CMP   orden,F_WRITE     ; acabar transferencia sector
  837.                JE    escritura
  838.                CMP   orden,F_VERIFY
  839.                JE    verificacion
  840.                CALL  leido?            ; realizar lectura...
  841.                JNC   ya_leido          ; sector ya en el buffer
  842. hay_que_leer:  CALL  acceso_sector     ; efectuar E/S
  843.                JC    acc_ret           ; ha habido fallo
  844. ya_leido:      CALL  trans_secc        ; buffer -> memoria
  845.                JMP   acc_ret
  846. escritura:     CMP   seccion,0
  847.                JNE   prelectura        ; sólo parte del sector cambia
  848.                CALL  num_secciones
  849.                CMP   secciones,AL
  850.                JAE   escribir          ; Todo el sector físico cambia
  851. prelectura:    CALL  leido?            ; Leer el sector físico para
  852.                JNC   escribir          ; cambiar sólo una parte de él
  853.                MOV   orden,F_READ      ; de momento leer...
  854.                CALL  acceso_sector     ; efectuar E/S
  855.                MOV   orden,F_WRITE     ; ... restaurar orden original
  856.                JC    acc_ret           ; ha habido fallo
  857. escribir:      CALL  trans_secc        ; memoria -> buffer
  858.                CALL  acceso_sector     ; volcar buffer al disco
  859.                JMP   acc_ret
  860. verificacion:  PUSH  BX
  861.                MOV   BL,seccion
  862.                CALL  num_secciones
  863. dec_sec_veri:  DEC   secciones
  864.                JZ    verifica
  865.                INC   BX
  866.                CMP   BL,AL
  867.                JB    dec_sec_veri
  868. verifica:      POP   BX
  869.                CALL  acceso_sector     ; leer para forzar verificación
  870. acc_ret:       PUSHF
  871.                INC   sector            ; preparado para otro sector
  872.                MOV   seccion,0         ; desde su primera sección
  873.                POPF
  874.                POP   AX
  875.                RET
  876.  
  877.               IFDEF  SUPERBOOT  ; SuperBOOT: válido el cruce del DMA
  878.  
  879. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  880.                AND   AL,AL
  881.                JZ    acc_mult_fin
  882.                MOV   AH,sector
  883.                MOV   sector_ini,AH
  884.                ADD   AL,AH
  885.                DEC   AX
  886.                MOV   sector_fin,AL
  887.                INC   AL
  888.                CALL  acceso_sector     ; sectores no problemáticos
  889.                MOV   sector,AL
  890. acc_mult_fin:  POP   AX
  891.                RET
  892.  
  893.               ELSE   ; No es SuperBOOT: Evitar cruce frontera DMA
  894.  
  895. acceso_multi:  PUSH  AX                ; AL = sectores a transferir
  896.                MOV   BX,ES             ;      desde 'sector' teniendo
  897.                XSHL  BX,4              ;      cuidado con el DMA
  898.                ADD   BX,DI
  899.                NEG   BX                ; BX = bytes hasta frontera DMA
  900.                CALL  num_secciones
  901.                MOV   CH,AL             ; AL secciones de 512 bytes
  902.                MOV   CL,0
  903.                SHL   CX,1              ; CX = bytes por sector
  904.                XCHG  AX,BX             ; BL = secciones por sector
  905.                XOR   DX,DX
  906.                DIV   CX
  907.                MOV   CL,AL             ; CL = sectores que caben
  908.                POP   AX                ; AL = sectores a transferir
  909.                CMP   AL,CL
  910.                JA    acc_mult2         ; no hay problemas con el DMA
  911. acc_mult1:     MOV   CL,AL
  912. acc_mult2:     AND   CL,CL
  913.                JZ    acc_mult3         ; primer sector problemático
  914.                MOV   AH,sector
  915.                MOV   sector_ini,AH
  916.                ADD   AH,CL
  917.                DEC   AH
  918.                MOV   sector_fin,AH
  919.                INC   AH
  920.                SUB   AL,CL
  921.                CALL  acceso_sector     ; sectores no problemáticos
  922.                MOV   sector,AH
  923.                JC    acc_mult_fin
  924. acc_mult3:     AND   AL,AL             ; ahora el sector problemático
  925.                JZ    acc_mult_fin
  926.                ADD   secciones,BL      ; compensar futuro decremento
  927.                CALL  acceso_secc       ; a través del buffer auxiliar
  928.                JC    acc_mult_fin
  929.                DEC   AL
  930.                JMP   acc_mult1         ; sectores que restan
  931. acc_mult_fin:  RET
  932.  
  933.               ENDIF
  934.  
  935. ejecuta_io     ENDP
  936.  
  937. ; ------------ Mover secciones desde el buffer hacia la memoria (con
  938. ;              orden F_READ) después de la lectura o de la memoria al
  939. ;              buffer (orden F_WRITE) antes de la escritura. En la
  940. ;              verificación (orden F_VERIFY) no se mueve nada porque
  941. ;              esta subrutina no es invocada.
  942.  
  943. trans_secc     PROC
  944.                XPUSH <AX, BX, CX, SI>  ; *
  945.                MOV   BL,seccion        ; desde esta sección
  946.                CALL  num_secciones     ; nº secciones del sector
  947. otra_secci:    PUSH  BX
  948.               IFDEF  XT
  949.                MOV   BH,BL
  950.                SHL   BH,1
  951.                MOV   BL,0
  952.               ELSE
  953.                XSHL  BX,9              ; sección * 512
  954.               ENDIF
  955.                ADD   BX,buffer         ; dirección
  956.                MOV   SI,BX
  957.                MOV   CX,256            ; tamaño sección (palabras)
  958.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  959.                REP   MOVSW             ; copiar 512 bytes
  960.                CALL  swap_reg          ; ¿intercambiar origen-destino?
  961.                POP   BX
  962.                DEC   secciones         ; una menos
  963.                JZ    fin_secc
  964.                INC   BX                ; otra sección del sector
  965.                CMP   BL,AL             ; ¿sector agotado?
  966.                JB    otra_secci        ; aún no
  967. fin_secc:      XPOP  <SI, CX, BX, AX>  ; *
  968.                RET
  969. swap_reg:      CMP   CS:orden,F_WRITE
  970.                JE    interc
  971.                CLC
  972.                RET
  973. interc:        XCHG  SI,DI             ; en escritura, invertir el
  974.                XPUSH <ES, DS>          ; sentido de la operación
  975.                XPOP  <ES, DS>
  976.                RET
  977. trans_secc     ENDP
  978.  
  979. ; ------------ Comprobar si el sector ya está en el buffer.
  980.  
  981. leido?         PROC
  982.                PUSH  AX
  983.                MOV   AL,buf_unidad
  984.                CMP   AL,unidad
  985.                JNE   no_leido          ; es en otra unidad
  986.                MOV   AL,cilindro
  987.                MOV   AH,cabezal
  988.                CMP   AX,buf_cilcab
  989.                JNE   no_leido          ; es en otro cilindro/cabezal
  990.                MOV   AL,buf_sector
  991.                CMP   AL,sector
  992.                JNE   no_leido          ; es otro sector
  993.                POP   AX
  994.                RET                     ; está en el buffer
  995. no_leido:      STC
  996.                POP   AX
  997.                RET                     ; sector no leído
  998. leido?         ENDP
  999.  
  1000. ; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
  1001. ;              sector correcto antes de llamar a sector_io.  En esta
  1002. ;              rutina se actualiza la variable «status» en función de
  1003. ;              los posibles errores de acceso.  Si sector_fin es
  1004. ;              distinto de 0 se accede a los sectores indicados, si es
  1005. ;              0 se accede sólo al sector «sector» a través del buffer
  1006. ;              intermedio y al final se anota el sector cargado ó
  1007. ;              escrito para evitar futuras lecturas innecesarias, a
  1008. ;              modo de mini-caché que dispara la velocidad de acceso a
  1009. ;              sectores lógicos consecutivos.
  1010.  
  1011. acceso_sector  PROC
  1012.                XPUSH <AX, BX>
  1013.                CALL  seek_drv          ; posicionar el cabezal
  1014.                JNC   en_pista
  1015.                CMP   status,0          ; ¿error ya determinado?
  1016.                JNE   acc_fin_err
  1017.                OR    status,40h        ; no: pues 'seek error'
  1018. acc_fin_err:   STC
  1019.                JMP   acceso_fin
  1020. en_pista:      CALL  pista0?
  1021.                MOV   AL,2
  1022.                JZ    tam_acc_ok        ; sectores 512 en cil./cab. 0
  1023.                LEA   BX,[SI].tabla_tsect
  1024.                ADD   BL,sector
  1025.                ADC   BH,0
  1026.                MOV   AL,[BX-1]
  1027. tam_acc_ok:    MOV   tsector,AL
  1028.                CMP   sector_fin,0      ; ¿usar buffer intermedio?
  1029.                JE    acceso_buffer
  1030.                CALL  sector_io
  1031.                MOV   sector_fin,0      ; no acceder a más de 1 sector
  1032.                PUSHF                   ; **1
  1033.                JMP   acceso_rep        ; en el futuro (por defecto)
  1034. acceso_buffer: XPUSH <ES, DI>
  1035.                PUSH  CS
  1036.                POP   ES
  1037.                MOV   DI,buffer         ; acceso con buffer auxiliar
  1038.                MOV   AL,sector         ; mismo sector inicial/final
  1039.                MOV   sector_ini,AL
  1040.                MOV   sector_fin,AL
  1041.                CALL  sector_io
  1042.                MOV   sector_fin,0
  1043.                XPOP  <DI, ES>
  1044.                PUSHF                   ; **2
  1045.                MOV   AL,-1             ; invalidar contenido buffer
  1046.                JC    acceso_anota      ; si hay error
  1047.                CMP   orden,F_VERIFY
  1048.                JE    acceso_rep        ; nada leído físicamente
  1049.                MOV   AL,unidad
  1050. acceso_anota:  MOV   buf_unidad,AL
  1051.                MOV   AL,cilindro
  1052.                MOV   AH,cabezal
  1053.                MOV   buf_cilcab,AX
  1054.                MOV   AL,sector
  1055.                MOV   buf_sector,AL     ; anotado el sector en buffer
  1056. acceso_rep:    POPF                    ; ** mucho cuidado con la pila
  1057.                CALL  set_err           ; ajustar variable «status»
  1058. acceso_fin:    XPOP  <BX, AX>
  1059.                RET
  1060. acceso_sector  ENDP
  1061.  
  1062. ; ------------ Devolver el número de secciones del sector en curso.
  1063.  
  1064. num_secciones  PROC
  1065.                CALL  pista0?
  1066.                MOV   AL,1
  1067.                JZ    num_secc_ok       ; sectores 512 en cil./cab. 0
  1068.                XPUSH <BX, CX>
  1069.                LEA   BX,[SI].tabla_tsect
  1070.                ADD   BL,sector
  1071.                ADC   BH,0
  1072.                MOV   CL,[BX-1]
  1073.                SUB   CL,2
  1074.                MOV   AL,1
  1075.                SHL   AL,CL
  1076.                XPOP  <CX, BX>
  1077. num_secc_ok:   RET                     ; resultado en AL
  1078. num_secciones  ENDP
  1079.  
  1080. ; ------------ Asegurar que el motor está en marcha.
  1081.  
  1082. motor_ok       PROC
  1083.                XPUSHA                  ; *
  1084.                PUSH  DS                ; **
  1085.                MOV   BX,40h
  1086.                PUSH  BX
  1087.                POP   DS
  1088.                MOV   CH,255-18         ; CH = 255 - 1 segundo
  1089.                CLI
  1090.                MOV   CL,CS:unidad
  1091.                MOV   AL,1
  1092.                SHL   AL,CL
  1093.                TEST  [BX-1],AL         ; ¿motor en marcha?
  1094.                JZ    arrancarlo        ; arrancarlo
  1095.                CMP   [BX],CH           ; Si encendido y acelerado...
  1096.                JBE   ok_motor          ; ...seguir
  1097. arrancarlo:    MOV   AH,CL
  1098.                MOV   CL,4
  1099.                SHL   AH,CL             ; unidad << 4
  1100.                OR    AL,AH
  1101.                MOV   [BX-1],AL         ; nuevo estado motores
  1102.                MOV   BYTE PTR [BX],255 ; asegurar que no se pare
  1103.                MOV   DX,FD_DOR         ; registro de salida digital
  1104.                ADD   CL,CS:unidad
  1105.                MOV   AL,1
  1106.                SHL   AL,CL             ; colocar bit del motor
  1107.                OR    AL,CS:unidad      ; seleccionar unidad
  1108.                OR    AL,00001100b      ; modo DMA, no hacer reset
  1109.                OUT   DX,AL             ; poner en marcha el motor
  1110.                STI
  1111.                MOV   AX,90FDh
  1112.                CLC
  1113.                INT   15h               ; permitir multitarea
  1114.                JC    ok_motor          ; timeout
  1115.                MOV   AX,1000           ; 1 segundo aceleración
  1116.                CALL  retardo           ; esperar aceleración disco
  1117. ok_motor:      MOV   [BX],CH           ; cuenta máxima detención motor
  1118.                STI                     ; sin forzar futura aceleración
  1119.                POP   DS                ; **
  1120.                XPOPA                   ; *
  1121.                RET
  1122. motor_ok       ENDP
  1123.  
  1124. ; ------------ Establecer modalidad de operación del controlador
  1125. ;              y poner el motor en marcha. Si CF=1 se le da tiempo
  1126. ;              además a la unidad para que acelere.
  1127.  
  1128. reset_drv      PROC
  1129.                XPUSHA
  1130.                CALL  motor_off_cnt     ; cuenta detención motor
  1131.               IFNDEF SUPERBOOT
  1132.                STC
  1133.                CALL  set_rate          ; velocidad correcta
  1134.               ENDIF
  1135.                MOV   CL,unidad
  1136.                MOV   AL,CL
  1137.                XSHL  AL,4              ; unidad seleccionada
  1138.                MOV   AH,1              ; bit de motor
  1139.                SHL   AH,CL             ; colocar dicho bit
  1140.                OR    AL,AH
  1141.                PUSH  DS                ; *
  1142.                DDS
  1143.                CLI
  1144.                MOV   DS:[3Fh],AL
  1145.                AND   BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
  1146.                POP   DS                ; *
  1147.                XSHL  AL,4              ; bits motor en nibble alto
  1148.                OR    AL,CL             ; seleccionar unidad
  1149.                OR    AL,00001000b      ; interrupciones+DMA y reset
  1150.                MOV   DX,FD_DOR         ; registro de salida digital
  1151.                OUT   DX,AL             ; señal de reset
  1152.               IFDEF  XT
  1153.                MOV   CX,50
  1154. respiro:       LOOP  respiro
  1155.               ELSE
  1156.                CALL  fdc_respiro       ; tiempo reconocer reset en 486
  1157.               ENDIF
  1158.                OR    AL,00000100b
  1159.                OUT   DX,AL             ; fin de señal de reset
  1160.                CALL  espera_int        ; rehabilitará interrupciones
  1161.                AND   status,7Fh        ; perdonar controladora rara
  1162.                MOV   AL,8
  1163.                CALL  fdc_write         ; comando 'leer estado int...'
  1164.                CALL  fdc_read
  1165.                CALL  fdc_read
  1166.                CALL  envia_specify     ; comando 'specify' adecuado
  1167.                XPOPA
  1168.                RET
  1169. reset_drv      ENDP
  1170.  
  1171. ; ------------ Enviar comando specify a la controladora. El step-rate
  1172. ;              se selecciona según la densidad, para evitar un sonido
  1173. ;              extraño al posicionar o recalibrar el cabezal.
  1174.  
  1175. envia_specify  PROC
  1176.                PUSH  AX
  1177.                PUSH  DS
  1178.                DDS
  1179.                MOV   AH,DS:[8Bh]
  1180.                POP   DS
  1181.                MOV   AL,3              ; comando 'specify'
  1182.                CALL  fdc_write
  1183.                MOV   AL,0BFh           ; step rate para 500 kbps
  1184.                AND   AH,11000000b
  1185.                JZ    spec1_ok
  1186.                MOV   AL,0AFh           ; step rate para 1 Mbps
  1187.                CMP   AH,11000000b
  1188.                JE    spec1_ok
  1189.                MOV   AL,0DFh           ; step rate para 250/300 Kbps
  1190. spec1_ok:      CALL  fdc_write
  1191.                MOV   AL,2
  1192.                CALL  fdc_write         ; head load y modo DMA
  1193.                POP   AX
  1194.                RET
  1195. envia_specify  ENDP
  1196.  
  1197. ; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
  1198. ;              entrar, se establece la mayor cuenta posible; en caso
  1199. ;              contrario, se pone el valor normal de la tabla base.
  1200.  
  1201. motor_off_cnt  PROC
  1202.                XPUSHA
  1203.                PUSH  DS
  1204.                MOV   AL,0FFh           ; valor máximo
  1205.                JC    motor_off_ok
  1206.               IFDEF  XT
  1207.                XOR   BX,BX
  1208.                MOV   DS,BX
  1209.               ELSE
  1210.                PUSH  0
  1211.                POP   DS
  1212.               ENDIF
  1213.                LDS   BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
  1214.                MOV   AL,[BX+2]               ; byte 2 tabla base disco
  1215. motor_off_ok:  DDS
  1216.                MOV   BYTE PTR DS:[40h],AL  ; cuenta parada motor
  1217.                POP   DS
  1218.                XPOPA
  1219.                RET
  1220. motor_off_cnt  ENDP
  1221.  
  1222. ; ------------ Llevar el cabezal a la pista indicada, recalibrando si
  1223. ;              hubo un reset (se invocó la función 0 de la INT 13h o
  1224. ;              se ejecutó reset_drv) antes de esta operación. Primero
  1225. ;              se selecciona la velocidad de transferencia y se borra
  1226. ;              el resultado de cualquier operación anterior, para que
  1227. ;              todo quede listo para el próximo acceso a disco.
  1228.  
  1229. seek_drv       PROC
  1230.                XPUSHA
  1231.               IFNDEF SUPERBOOT
  1232.                CLC
  1233.               ENDIF
  1234.                CALL  set_rate          ; velocidad / borrar resultados
  1235.                CALL  envia_specify     ; comando 'specify' adecuado
  1236.                MOV   AH,1
  1237.                MOV   CL,unidad
  1238.                SHL   AH,CL             ; AH = 1 (A:) ó 2 (B:)
  1239.                PUSH  DS
  1240.                DDS
  1241.                TEST  AH,DS:[3Eh]
  1242.                POP   DS
  1243.                JNZ   do_seek           ; la unidad ya fue recalibrada
  1244.                CALL  recalibrar
  1245.                JC    fallo_seek        ; fallo al recalibrar
  1246. do_seek:       MOV   BX,94h
  1247.                ADD   BL,unidad
  1248.                MOV   AL,cilindro
  1249.                PUSH  DS                ; *
  1250.                DDS
  1251.                OR    DS:[3Eh],AH       ; unidad ya recalibrada
  1252.                MOV   AH,DS:[41h]       ; código de error previo
  1253.                CMP   AL,[BX]
  1254.                MOV   [BX],AL
  1255.                POP   DS                ; *
  1256.                JNE   hacer_seek        ; seek necesario
  1257.                CMP   AH,40h            ; ¿error de seek previo?
  1258.                JNE   seek_ok           ; no, evitar seek innecesario
  1259. hacer_seek:    MOV   AL,0Fh
  1260.                CALL  fdc_write         ; comando 'seek'
  1261.                JC    fallo_seek
  1262.                MOV   AL,cabezal
  1263.                XSHL  AL,2
  1264.                OR    AL,unidad
  1265.                CALL  fdc_write         ; enviar HD, US1, US0
  1266.                MOV   AL,cilindro
  1267.                CALL  fdc_write         ; enviar cilindro
  1268.                CALL  espera_int        ; esperar interrupción
  1269.                JC    fallo_seek
  1270.                MOV   AL,8
  1271.                CALL  fdc_write         ; comando 'leer estado int...'
  1272.                JC    fallo_seek
  1273.                CALL  fdc_read          ; leer registro de estado 0
  1274.                JC    fallo_seek
  1275.                MOV   AH,AL
  1276.                CALL  fdc_read          ; leer cilindro actual
  1277.                TEST  AH,11000000b      ; comprobar ST0
  1278.                JNZ   fallo_seek
  1279.                MOV   AL,15             ; estabilización para escritura
  1280.                CMP   orden,F_WRITE
  1281.                JE    rseek_ok
  1282.                MOV   AL,1              ; estabilización para lectura
  1283. rseek_ok:      CBW                     ; AH = 0
  1284.                CALL  retardo           ; esperar asentamiento cabezal
  1285. seek_ok:       XPOPA
  1286.                CLC                     ; retornar con éxito
  1287.                RET
  1288. fallo_seek:    XPOPA
  1289.                STC                     ; retornar indicando fallo
  1290.                RET
  1291. seek_drv       ENDP
  1292.  
  1293. ; ------------ Establecer velocidad de transferencia correcta si aún
  1294. ;              no ha sido seleccionada y borrar el resultado de otra
  1295. ;              operación previa.  Si CF=1 al entrar, la velocidad se
  1296. ;              establece  incondicionalmente  (por si la variable de
  1297. ;              la BIOS no está correctamente asignada),  salvo en el
  1298. ;              código SuperBOOT (por razones de espacio).
  1299.  
  1300. set_rate       PROC
  1301.                XPUSHA
  1302.               IFNDEF SUPERBOOT
  1303.                PUSHF
  1304.               ENDIF
  1305.                CALL  pista0?
  1306.                MOV   AX,[SI].vunidad   ; velocidad pista 0 / demás
  1307.                JZ    vel_ok
  1308.                MOV   AL,AH
  1309. vel_ok:       IFNDEF SUPERBOOT
  1310.                POPF
  1311.               ENDIF
  1312.                PUSH  DS                ; *
  1313.                DDS
  1314.               IFNDEF SUPERBOOT
  1315.                JC    abs_rate
  1316.               ENDIF
  1317.                MOV   AH,DS:[8Bh]
  1318.               IFDEF  XT
  1319.                MOV   CL,6
  1320.                SHR   AH,CL
  1321.               ELSE
  1322.                SHR   AH,6              ; aislar bits de velocidad
  1323.               ENDIF
  1324.                CMP   AL,AH
  1325.                JE    vel_set           ; velocidad ya seleccionada
  1326. abs_rate:      MOV   DX,FD_DCR
  1327.                OUT   DX,AL             ; seleccionarla
  1328.                XSHL  AL,6
  1329.                AND   BYTE PTR DS:[8Bh],00111111b
  1330.                OR    DS:[8Bh],AL
  1331. vel_set:       POP   DS                ; *
  1332.                LEA   DI,status
  1333.                MOV   CX,8
  1334. borra_status:  MOV   [DI],CH           ; borrar información de estado
  1335.                INC   DI
  1336.                LOOP  borra_status
  1337.                XPOPA
  1338.                RET
  1339. set_rate       ENDP
  1340.  
  1341. ; ------------ Recalibrar la unidad (si hay error se intenta otra vez
  1342. ;              para el caso de que deba moverse más de 77 pistas).
  1343.  
  1344. recalibrar     PROC
  1345.                XPUSHA
  1346.                MOV   BX,94h
  1347.                ADD   BL,unidad
  1348.                PUSH  DS                ; *
  1349.                DDS
  1350.                MOV   [BX],BH           ; pista actual = 0
  1351.                POP   DS                ; *
  1352.                MOV   CX,2              ; dos veces como mucho
  1353. recalibra:     MOV   AL,7
  1354.                CALL  fdc_write         ; comando de 'recalibrado'
  1355.                JC    fallo_recal
  1356.                MOV   AL,cabezal
  1357.                XSHL  AL,2
  1358.                OR    AL,unidad
  1359.                CALL  fdc_write         ; enviar HD, US1, US0
  1360.                JC    fallo_recal
  1361.                CALL  espera_int        ; esperar interrupción
  1362.                JC    fallo_recal
  1363.                MOV   AL,8
  1364.                CALL  fdc_write         ; comando 'leer estado int...'
  1365.                JC    fallo_recal
  1366.                CALL  fdc_read          ; leer registro de estado 0
  1367.                JC    fallo_recal
  1368.                MOV   AH,AL
  1369.                CALL  fdc_read          ; leer cilindro actual
  1370.                XOR   AH,00100000b      ; bajar bit de 'seek end'
  1371.                TEST  AH,11110000b      ; comprobar resultado y ST0
  1372.                JNZ   fallo_recal       ; sin 'seek end' o TRK0
  1373.                MOV   AX,1              ; pausa de 1 ms
  1374.                CALL  retardo
  1375.                JMP   recal_ret
  1376. fallo_recal:   LOOP  recalibra         ; reintentar comando
  1377.                STC                     ; condición de fallo
  1378. recal_ret:     XPOPA
  1379.                RET
  1380. recalibrar     ENDP
  1381.  
  1382. ; ------------ Cargar o escribir sector(es) del disco en ES:DI,
  1383. ;              actualizando la dirección en ES:DI pero sin alterar
  1384. ;              ningún otro registro. Si hay error se devuelve CF=1 y
  1385. ;              no se modifica ES:DI. A partir de fdc_result se dejan
  1386. ;              los 7 bytes que devuelve el FDC al final del acceso.
  1387. ;              En caso de verificación (F_VERIFY) se programa el DMA
  1388. ;              para que no realice transferencia física (convenio de
  1389. ;              las BIOS con fecha 15/11/85 y posterior).
  1390.  
  1391. sector_io      PROC
  1392.                XPUSH <AX, BX, CX, DX>
  1393.                MOV   CL,tsector
  1394.                MOV   CH,0
  1395.                STC
  1396.                RCL   CH,CL
  1397.                MOV   CL,0              ; nº de bytes por sector
  1398.                MOV   AL,sector_fin
  1399.                SUB   AL,sector_ini
  1400.                INC   AX
  1401.                CBW                     ; AX sectores (AH = 0)
  1402.                MUL   CX
  1403.                MOV   DX,AX             ; bytes totales
  1404.                MOV   CX,AX
  1405.                DEC   CX                ; bytes totales - 1
  1406.                MOV   AX,ES
  1407.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1408.               IFDEF  SUPERBOOT
  1409.                JC    sector_io_ko      ; chequear cruce frontera DMA
  1410.               ENDIF
  1411.                MOV   AL,orden          ; modo DMA necesario
  1412.                CALL  prepara_DMA
  1413.                CMP   AL,F_WRITE
  1414.                MOV   AL,11000101b      ; comando de escritura del FDC
  1415.                JE    orden_io_ok
  1416.                MOV   AL,11100110b      ; comando leer (verif.) del FDC
  1417. orden_io_ok:   CALL  fdc_write         ; comando leer/escribir del FDC
  1418.                JC    sector_io_ko
  1419.                MOV   AL,cabezal
  1420.                XSHL  AL,2
  1421.                OR    AL,unidad
  1422.                CALL  fdc_write         ; byte 1 de la orden
  1423.                MOV   AL,cilindro
  1424.                CALL  fdc_write         ; enviar cilindro
  1425.                MOV   AL,cabezal
  1426.                CALL  fdc_write         ; enviar cabezal
  1427.                MOV   AL,sector_ini
  1428.                CALL  fdc_write         ; enviar nº sector
  1429.                MOV   AL,tsector
  1430.                CALL  fdc_write         ; longitud sector
  1431.                MOV   AL,sector_fin
  1432.                CALL  fdc_write         ; último sector
  1433.                MOV   AL,[SI].gap
  1434.                CALL  fdc_write         ; GAP de lectura/escritura
  1435.                MOV   AL,128
  1436.                CALL  fdc_write         ; tamaño sector si longitud=0
  1437.                CALL  espera_int
  1438.                PUSHF                   ; *
  1439.                LEA   BX,fdc_result
  1440.                MOV   CX,7
  1441. sect_io_res:   CALL  fdc_read          ; leyendo resultados
  1442.                MOV   [BX],AL
  1443.                INC   BX
  1444.                LOOP  sect_io_res
  1445.                POPF                    ; *
  1446.                JC    sector_io_ko
  1447.                TEST  fdc_result,11000000b
  1448.                JNZ   sector_io_ko
  1449.                ADD   DI,DX             ; actualizar dirección
  1450.                CLC                     ; Ok
  1451.                JMP   sector_io_fin
  1452. sector_io_ko:  STC                     ; indicar fallo
  1453. sector_io_fin: XPOP  <DX, CX, BX, AX>
  1454.                RET
  1455. sector_io      ENDP
  1456.  
  1457. ; ------------ Devolver en AH la página de DMA y en BX la base. A la
  1458. ;              entrada, AX:DI -> dirección de memoria y CX = bytes-1.
  1459. ;              Se supone que el buffer no cruza una frontera de DMA,
  1460. ;              aunque el código SuperBOOT devuelve error en ese caso.
  1461.  
  1462. calc_dir_DMA   PROC
  1463.                PUSH  DX
  1464.                MOV   BX,16
  1465.                MUL   BX
  1466.                ADD   AX,DI
  1467.                ADC   DX,0              ; DX:AX = dirección 20 bits
  1468.                MOV   BX,AX             ; base en BX
  1469.                MOV   AH,DL             ; página
  1470.               IFDEF  SUPERBOOT
  1471.                MOV   DX,CX             ; comprobar cruce en SuperBOOT
  1472.                ADD   DX,BX
  1473.                JNC   dir_DMA_ok
  1474.                MOV   status,9          ; error de frontera de DMA
  1475.               ENDIF
  1476. dir_DMA_ok:    POP   DX
  1477.                RET
  1478. calc_dir_DMA   ENDP
  1479.  
  1480. ; ------------ Crear tabla con información para formatear. En ES:BX
  1481. ;              está el futuro sector de arranque del disquete.
  1482.  
  1483.               IFNDEF SUPERBOOT  ; en SuperBOOT no se formatea
  1484.  
  1485. genera_info    PROC
  1486.                XPUSHA
  1487.                MOV   buf_unidad,-1     ; invalidar contenido buffer
  1488.                MOV   SI,buffer
  1489.                MOV   DI,BX
  1490.                CALL  pista0?
  1491.                JNZ   no_cilcab0             ; no es cilindro/cabezal 0
  1492.                ADD   DI,ES:[BX+70]          ; DI -> datos pista 0
  1493.                MOV   CL,ES:[DI]
  1494.                MOV   CH,0                   ; CX sectores en pista 0
  1495.                INC   DI
  1496.                MOV   AL,ES:[DI]             ; GAP para pista 0
  1497.                MOV   AH,0F6h                ; byte de relleno
  1498.                INC   DI
  1499.                MOV   BYTE PTR [SI],2        ; tamaño de sector
  1500.                MOV   BYTE PTR [SI+1],CL     ; número de sectores
  1501.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1502. genera_0:      ADD   SI,4
  1503.                MOV   AL,cilindro
  1504.                MOV   AH,cabezal
  1505.                MOV   [SI],AX                ; datos para cada sector
  1506.                MOV   AL,ES:[DI]
  1507.                MOV   AH,2                   ; LOG2 (tamaño)-7
  1508.                INC   DI
  1509.                MOV   [SI+2],AX              ; nº de sector / tamaño
  1510.                LOOP  genera_0
  1511.                XPOPA
  1512.                RET
  1513. no_cilcab0:    ADD   DI,ES:[BX+72]
  1514.                CMP   BYTE PTR ES:[BX+65],1
  1515.                JE    info_stv
  1516.                MOV   DL,ES:[DI+2]           ; tamaño /F
  1517.                MOV   DH,ES:[DI]             ; nº sectores
  1518.                MOV   [SI],DX
  1519.                XCHG  DH,DL                  ; tamaño en DH
  1520.                MOV   CL,DL
  1521.                MOV   CH,0                   ; CX sectores
  1522.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1523.                MOV   AH,0F6h                ; byte relleno /F
  1524.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1525.                PUSH  DX
  1526.                MOV   AX,ES:[DI+3]
  1527.                PUSH  AX
  1528.                ADD   AL,AH
  1529.                MUL   cilindro
  1530.                MOV   DX,AX
  1531.                POP   AX
  1532.                MUL   cabezal
  1533.                ADD   AX,DX
  1534.                XOR   DX,DX
  1535.                MOV   BL,ES:[DI]
  1536.                MOV   BH,0
  1537.                DIV   BX                ; DL = módulo
  1538.                SUB   DL,ES:[DI]
  1539.                NEG   DL
  1540.                MOV   AL,DL
  1541.                POP   DX                ; restaurar tamaño en DH
  1542.                MOV   DL,AL             ; primer sector de la pista - 1
  1543.                MOV   BL,ES:[DI]        ; nº sectores en la pista
  1544. genera_pn:     ADD   SI,4
  1545.                INC   DX
  1546.                CMP   DL,BL
  1547.                JBE   ns_ok
  1548.                MOV   DL,1              ; empezar desde el 1
  1549. ns_ok:         MOV   AL,cilindro
  1550.                MOV   AH,cabezal
  1551.                MOV   [SI],AX           ; datos para cada sector
  1552.                MOV   [SI+2],DX         ; nº sector / LOG2 (tamaño)-7
  1553.                LOOP  genera_pn
  1554.                XPOPA
  1555.                RET
  1556. info_stv:      MOV   CH,ES:[DI]             ; nº sectores
  1557.                MOV   CL,0                   ; CL:CH sectores
  1558.                MOV   [SI],CX                ; tamaño (CL=0) y número
  1559.                XCHG  CH,CL                  ; CX sectores
  1560.                MOV   AL,ES:[DI+1]           ; GAP para formatear
  1561.                MOV   AH,4Eh                 ; byte de relleno /M
  1562.                MOV   [SI+2],AX              ; GAP / byte de relleno
  1563.                MOV   DL,128
  1564. genera_otro:   ADD   SI,4
  1565.                INC   DX
  1566.                MOV   AL,cilindro
  1567.                MOV   AH,cabezal
  1568.                MOV   [SI],AX           ; datos para cada sector
  1569.                XPUSH <CX, DI>          ; *
  1570.                MOV   CL,ES:[DI+2]      ; CH está a 0
  1571. busca_num:     ADD   DI,3
  1572.                CMP   DL,ES:[DI]
  1573.                MOV   AX,ES:[DI+1]      ; número de sector / tamaño
  1574.                JE    hallado           ; es sector a cambiar número
  1575.                LOOP  busca_num
  1576.                MOV   AL,DL             ; no cambiar número
  1577.                MOV   AH,0              ; e indicar tamaño 128
  1578. hallado:       XPOP  <DI, CX>          ; *
  1579.                MOV   [SI+2],AX         ; nº sector / LOG2 (tamaño)-7
  1580.                LOOP  genera_otro
  1581.                XPOPA
  1582.                RET
  1583. genera_info    ENDP
  1584.  
  1585. ; ------------ Formatear una pista.
  1586.  
  1587. formatea_pista PROC
  1588.                XPUSHA
  1589.                MOV   BX,buffer
  1590.                MOV   DI,BX
  1591.                MOV   CL,[BX+1]
  1592.                MOV   CH,0              ; CX sectores
  1593.                XSHL  CX,2
  1594.                DEC   CX                ; nº de bytes - 1
  1595.                MOV   AX,DS
  1596.                CALL  calc_dir_DMA      ; AX:DI -> base BX y página AH
  1597.                MOV   AL,4Ah            ; modo DMA para escribir
  1598.                ADD   BX,4              ; saltar primeros 4 bytes
  1599.                CALL  prepara_DMA
  1600.                MOV   BX,buffer
  1601.                MOV   AL,F_FORMAT
  1602.                CALL  fdc_write
  1603.                JC    fallo_fmt
  1604.                MOV   AL,cabezal
  1605.                XSHL  AL,2
  1606.                OR    AL,unidad
  1607.                CALL  fdc_write         ; byte 1 de la orden
  1608.                JC    fallo_fmt
  1609.                MOV   CX,4
  1610. format_cmd:    MOV   AL,[BX]
  1611.                CALL  fdc_write
  1612.                INC   BX
  1613.                LOOP  format_cmd
  1614.                CALL  espera_int
  1615. fallo_fmt:     PUSHF
  1616.                LEA   BX,fdc_result
  1617.                MOV   CX,7
  1618. format_res:    CALL  fdc_read          ; leyendo resultados
  1619.                MOV   [BX],AL
  1620.                INC   BX
  1621.                LOOP  format_res
  1622.                POPF
  1623.                JC    fallo_format
  1624.                TEST  fdc_result,11000000b
  1625.                JZ    format_ret
  1626. fallo_format:  STC                     ; fallo
  1627. format_ret:    XPOPA
  1628.                RET
  1629. formatea_pista ENDP
  1630.  
  1631.               ENDIF
  1632.  
  1633. ; ------------ Esperar interrupción de disquete durante casi 2
  1634. ;              segundos antes de considerar que ha sido un fracaso.
  1635.  
  1636.               IFNDEF XT
  1637.  
  1638. espera_int     PROC
  1639.                STI
  1640.                XPUSHA
  1641.                PUSH  DS
  1642.                DDS
  1643.                MOV   AX,9001h
  1644.                CLC
  1645.                INT   15h               ; permitir multitarea
  1646.                MOV   DX,0280h
  1647.                MOV   BX,3Eh
  1648.                JC    timeout_int
  1649. esp_int_1s:    XOR   CX,CX
  1650. esp_int:       TEST  [BX],DL           ; ¿llegó la interrupción?
  1651.                JNZ   fin_espera
  1652.                PMICRO
  1653.                LOOP  esp_int           ; esperar durante casi 1 seg.
  1654.                DEC   DH
  1655.                JNZ   esp_int_1s
  1656. timeout_int:   OR    CS:status,DL      ; timeout
  1657.                STC
  1658. fin_espera:    PUSHF
  1659.                AND   BYTE PTR [BX],7Fh ; para la próxima vez
  1660.                POPF
  1661.                POP   DS
  1662.                XPOPA
  1663.                RET
  1664. espera_int     ENDP
  1665.  
  1666.               ELSE   ; Si es XT...
  1667.  
  1668. espera_int     PROC
  1669.                STI
  1670.                XPUSHA
  1671.                XPUSH <DS, 40h>
  1672.                POP   DS
  1673.                MOV   AH,0FFh
  1674. esperar_int:   CMP   AL,DS:[6Ch]
  1675.                JE    mira_int
  1676.                MOV   AL,DS:[6Ch]
  1677.                INC   AH
  1678.                CMP   AH,37             ; ¿más de 2 segundos?
  1679.                JB    mira_int
  1680. timeout_int:   OR    CS:status,80h     ; timeout
  1681.                STC
  1682.                JMP   fin_espera
  1683. mira_int:      TEST  BYTE PTR DS:[3Eh],80h
  1684.                JZ    esperar_int
  1685.                AND   BYTE PTR DS:[3Eh],7Fh  ; CF=0
  1686. fin_espera:    POP   DS
  1687.                XPOPA
  1688.                RET
  1689. espera_int     ENDP
  1690.  
  1691.               ENDIF
  1692.  
  1693. ; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
  1694. ;              base, AH = registro de página y CX = nº bytes - 1.
  1695.  
  1696. prepara_DMA    PROC
  1697.                PUSH  AX
  1698.                CLI
  1699.                OUT   0Bh,AL            ; registro de modo del DMA
  1700.                MOV   AL,0
  1701.                DELAY
  1702.                OUT   0Ch,AL            ; clear first/last flip-flop
  1703.                MOV   AL,BL
  1704.                DELAY
  1705.                OUT   4,AL
  1706.                MOV   AL,BH
  1707.                DELAY
  1708.                OUT   4,AL              ; enviada dirección base
  1709.                DELAY
  1710.                MOV   AL,AH
  1711.                OUT   81h,AL            ; registro de página del DMA
  1712.                MOV   AL,CL
  1713.                DELAY
  1714.                OUT   5,AL
  1715.                MOV   AL,CH
  1716.                DELAY
  1717.                OUT   5,AL              ; enviada cuenta de bytes
  1718.                STI
  1719.                MOV   AL,2
  1720.                DELAY
  1721.                OUT   0Ah,AL            ; habilitar canal 2 de DMA
  1722.                POP   AX
  1723.                RET
  1724. prepara_DMA    ENDP
  1725.  
  1726. ; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
  1727. ;              la operación fracasó (el FDC no estaba listo) y
  1728. ;              se indica la condición de timeout en «status».
  1729.  
  1730.               IFNDEF XT
  1731.  
  1732. fdc_read       PROC
  1733.                XPUSH <CX, DX, AX>
  1734.                CALL  fdc_respiro       ; no abrasar el FDC
  1735.                MOV   DX,FD_STATUS      ; registro de estado del FDC
  1736.                MOV   CX,133            ; constante para 0,002 segundos
  1737. espera_rd:     DELAY
  1738.                IN    AL,DX
  1739.                AND   AL,11000000b
  1740.                CMP   AL,11000000b      ; ¿dato listo?
  1741.                JE    fdc_rd_ok
  1742.                DELAY
  1743.                IN    AL,61h
  1744.                AND   AL,10h
  1745.                CMP   AL,AH
  1746.                JE    espera_rd         ; reintentarlo durante 15,09 µs
  1747.                MOV   AH,AL
  1748.                LOOP  espera_rd
  1749.                XPOP  <AX, DX, CX>
  1750.                OR    status,80h        ; timeout
  1751.                MOV   AL,0
  1752.                STC                     ; fallo
  1753.                RET
  1754. fdc_rd_ok:     POP   AX
  1755.                INC   DX                ; apuntar al registro de datos
  1756.                DELAY
  1757.                IN    AL,DX             ; leer byte del FDC
  1758.                XPOP  <DX, CX>
  1759.                CLC                     ; Ok
  1760.                RET
  1761. fdc_read       ENDP
  1762.  
  1763.               ELSE
  1764.  
  1765. fdc_read       PROC
  1766.                XPUSH <CX, DX>
  1767.                MOV   DX,FD_STATUS      ; registro de estado del FDC
  1768.                XOR   CX,CX             ; evitar cuelgue total si falla
  1769. espera_rd:     IN    AL,DX             ; leer registro de estado
  1770.                TEST  AL,80h            ; ¿bit 7 inactivo?
  1771.                LOOPZ espera_rd         ; así es: el FDC está ocupado
  1772.                JCXZ  fdc_rd_nok
  1773.                INC   DX                ; apuntar al registro de datos
  1774.                IN    AL,DX             ; leer byte del FDC
  1775.                CLC
  1776.                XPOP  <DX, CX>
  1777.                RET
  1778. fdc_rd_nok:    OR    status,80h        ; timeout
  1779.                STC
  1780.                XPOP  <DX, CX>
  1781.                RET
  1782. fdc_read       ENDP
  1783.  
  1784.               ENDIF
  1785.  
  1786. ; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
  1787. ;              la operación fracasó (el FDC no estaba listo) y
  1788. ;              se indica la condición de timeout en «status».
  1789.  
  1790.               IFNDEF XT
  1791.  
  1792. fdc_write      PROC
  1793.                XPUSH <CX, DX, AX>
  1794.                CALL  fdc_respiro       ; no abrasar el FDC
  1795.                MOV   DX,FD_STATUS      ; registro de estado del FDC
  1796.                MOV   CX,133            ; constante para 0,002 segundos
  1797. espera_wr:     DELAY
  1798.                IN    AL,DX
  1799.                TEST  AL,80h            ; ¿listo para E/S?
  1800.                JNZ   fdc_wr_ok
  1801.                DELAY
  1802.                IN    AL,61h
  1803.                AND   AL,10h
  1804.                CMP   AL,AH
  1805.                JE    espera_wr         ; reintentarlo durante 15,09 µs
  1806.                MOV   AH,AL
  1807.                LOOP  espera_wr
  1808.                XPOP  <AX, DX, CX>
  1809.                OR    status,80h        ; timeout
  1810.                STC                     ; fallo
  1811.                RET
  1812. fdc_wr_ok:     INC   DX                ; apuntar al registro de datos
  1813.                POP   AX
  1814.                DELAY
  1815.                OUT   DX,AL             ; enviar byte al FDC
  1816.                XPOP  <DX, CX>
  1817.                CLC                     ; Ok
  1818.                RET
  1819. fdc_write      ENDP
  1820.  
  1821.               ELSE
  1822.  
  1823. fdc_write      PROC
  1824.                XPUSH <AX, CX, DX>
  1825.                MOV   DX,FD_STATUS      ; registro de estado del FDC
  1826.                XCHG  AH,AL             ; preservar AL en AH
  1827.                XOR   CX,CX             ; evitar cuelgue total si falla
  1828. espera_wr:     IN    AL,DX             ; leer registro de estado
  1829.                TEST  AL,80h            ; ¿bit 7 inactivo?
  1830.                LOOPZ espera_wr         ; así es: el FDC está ocupado
  1831.                JCXZ  fdc_wr_nok
  1832.                XCHG  AH,AL             ; recuperar el dato de AL
  1833.                INC   DX                ; apuntar al registro de datos
  1834.                OUT   DX,AL             ; enviar byte al FDC
  1835.                XPOP  <DX, CX, AX>
  1836.                CLC
  1837.                RET
  1838. fdc_wr_nok:    OR    status,80h        ; timeout
  1839.                XPOP  <DX, CX, AX>
  1840.                STC
  1841.                RET
  1842. fdc_write      ENDP
  1843.  
  1844.               ENDIF
  1845.  
  1846. ; ------------ Retardo de 60 µs para dar tiempo al FDC en 486 rápidos.
  1847.  
  1848.               IFNDEF XT
  1849.  
  1850. fdc_respiro    PROC
  1851.                XPUSH <AX, CX>
  1852.                MOV   CX,4
  1853. fdc_ret:       PMICRO
  1854.                LOOP  fdc_ret
  1855.                XPOP  <CX, AX>
  1856.                RET
  1857. fdc_respiro    ENDP
  1858.  
  1859.               ENDIF
  1860.  
  1861. ; ------------ Esperar exactamente AX milisegundos.
  1862.  
  1863.               IFNDEF XT
  1864.  
  1865. retardo        PROC
  1866.                PUSHF
  1867.                XPUSHA
  1868.                MOV   DX,16970          ; 16970 = 1193180/18*256/1000
  1869.                MUL   DX
  1870.                MOV   CL,AH             ; dividir DX:AX entre 256 y
  1871.                MOV   CH,DL             ; dejar el resultado en DX:CX
  1872.                MOV   DL,DH
  1873.                MOV   DH,0              ; DX:CX 15,09 µs-avos
  1874. retardando:    PMICRO
  1875.                LOOP  retardando
  1876.                AND   DX,DX
  1877.                JZ    retardado
  1878.                DEC   DX
  1879.                JMP   retardando
  1880. retardado:     XPOPA
  1881.                POPF
  1882.                RET
  1883. retardo        ENDP
  1884.  
  1885.               ELSE
  1886.  
  1887. retardo        PROC
  1888.                PUSHF
  1889.                XPUSH <AX, BX, CX, DX>
  1890. retarda_mas:   CMP   AX,54             ; como máximo 54 ms cada vez
  1891.                JBE   retarda_fin
  1892.                PUSH  AX
  1893.                MOV   AX,54
  1894.                CALL  rt_ax
  1895.                POP   AX
  1896.                SUB   AX,54
  1897.                JMP   retarda_mas
  1898. retarda_fin:   CALL  rt_ax
  1899.                XPOP  <DX, CX, BX, AX>
  1900.                POPF
  1901.                RET
  1902.  
  1903. rt_ax:         MOV   DX,1000           ; retardo de hasta 54 ms
  1904.                MUL   DX
  1905.                MUL   CS:tbase
  1906.                MOV   CX,54925
  1907.                DIV   CX                ; AX = contador iteraciones
  1908.                MOV   CX,AX
  1909.                EVEN                    ; forzar alineamiento
  1910. retarda:       DEC   CX
  1911.                JMP   SHORT $+2
  1912.                JNZ   retarda
  1913.                RET
  1914. retardo        ENDP
  1915.  
  1916.               ENDIF
  1917.  
  1918.               IFDEF  SUPERBOOT
  1919.  
  1920. ; ------------ Esta subrutina sustituye a la macro PMICRO en el
  1921. ;              código SuperBOOT por razones de espacio.
  1922.  
  1923. pmicro_iter:   DELAY                   ; retardo de aprox. 15,09 µs
  1924.                IN    AL,61h            ; (exactamente 18/1193180 sg.)
  1925.                AND   AL,10h            ; La rutina se puede ejecutar
  1926.                CMP   AL,AH             ; repetitivamente (se apoya en
  1927.                JE    pmicro_iter       ; AX) para hacer retardos a
  1928.                MOV   AH,AL             ; través de la temporización
  1929.                RET                     ; del refresco de la memoria
  1930.  
  1931. ; ------------ Código invocado durante el SuperBOOT desde 2MBOOTHD.ASM
  1932. ;              A la entrada: CS=ES, SS=0 y AX = tipo unidades.
  1933.  
  1934. initcode:      PUSH  DS
  1935.                PUSH  SS
  1936.                POP   DS
  1937.                MOV   ES:[info_A.tipo_drv],AL  ; anotar tipo de A:
  1938.                MOV   ES:[info_B.tipo_drv],AH  ; anotar tipo de B:
  1939.                LEA   DI,ant_int13
  1940.                MOV   SI,13h*4              ; vector de INT 13h
  1941.                CLD
  1942.                CLI
  1943.                MOVSW
  1944.                MOVSW                       ; anotada dirección INT 13h
  1945.                MOV   WORD PTR [SI-4],OFFSET ges_int13
  1946.                MOV   [SI-2],ES
  1947.                STI                         ; desviada INT 13h
  1948.                POP   DS
  1949.                RETF                        ; volver a 2MBOOTHD
  1950.  
  1951. ant_int13      LABEL DWORD        ; vector de la INT 13h previa
  1952. ant_int13_off  DW    initcode
  1953. ant_int13_seg  DW    0AA55h       ; significa "SuperBOOT correcto"
  1954.  
  1955.               ENDIF
  1956.  
  1957.                ; --- Todo esto ocupa 2560 bytes exactos.
  1958.  
  1959.                ; --- Ubicación del sector de hasta 2048 bytes.
  1960.  
  1961.                EVEN
  1962. buffer_io      EQU   $
  1963. tbuffer        EQU   2048
  1964.